package org.little.beans.factory.support;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.little.beans.BeanDefinition;
import org.little.beans.BeansException;
import org.little.beans.PropertyValue;
import org.little.beans.SimpleTypeConverter;
import org.little.beans.factory.BeanCreationException;
import org.little.beans.factory.BeanFactoryAware;
import org.little.beans.factory.NoSuchBeanDefinitionException;
import org.little.beans.factory.config.BeanPostProcessor;
import org.little.beans.factory.config.DependencyDescriptor;
import org.little.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.little.util.ClassUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultBeanFactory extends AbstractBeanFactory implements BeanDefinitionRegistry {
    private static final Log logger = LogFactory.getLog(DefaultBeanFactory.class);

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ClassLoader beanClassLoader;
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public DefaultBeanFactory() {}

    @Override
    public void registerBeanDefinition(String beanId, BeanDefinition beanDefinition) {
        this.beanDefinitionMap.put(beanId, beanDefinition);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanId) {
        return this.beanDefinitionMap.get(beanId);
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor postProcessor) {
        this.beanPostProcessors.add(postProcessor);
    }

    @Override
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }

    @Override
    public Object getBean(String beanId) {
        // 从 beanDefinitionMap 取出当前 BeanDefinition
        BeanDefinition beanDefinition = this.getBeanDefinition(beanId);
        if (beanDefinition == null) {
            throw new BeanCreationException("Bean Definition does not exist");
        }

        if (beanDefinition.isSingleton()) {
            Object bean = this.getSingleton(beanId);
            if (bean == null) {
                bean = createBean(beanDefinition);
                this.registerSingleton(beanId, bean);
            }
            return bean;
        }
        return createBean(beanDefinition);
    }

    @Override
    protected Object createBean(BeanDefinition beanDefinition) {
        // 创建实例
        Object bean = instantiateBean(beanDefinition);
        // 设置属性
        populateBean(beanDefinition, bean);
        // 初始化
        bean = initializeBean(beanDefinition, bean);
        return bean;
    }

    private Object instantiateBean(BeanDefinition beanDefinition) {

        if (beanDefinition.hasConstructorArgumentValues()) {
            // 有构造函数就用
            ConstructorResolver resolver = new ConstructorResolver(this);
            return resolver.autowireConstructor(beanDefinition);
        } else {
            ClassLoader classLoader = this.getBeanClassLoader();
            String beanClassName = beanDefinition.getBeanClassName();
            try {
                Class<?> clazz = classLoader.loadClass(beanClassName);
                // 通过反射装载类 （要有缺省的构造函数）
                return clazz.newInstance();
            } catch (Exception e) {
                throw new BeanCreationException("create bean for " + beanClassName + " failed", e);
            }
        }
    }

    protected void populateBean(BeanDefinition beanDefinition, Object bean) {

        for (BeanPostProcessor processor : this.getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                ((InstantiationAwareBeanPostProcessor) processor).postProcessPropertyValues(bean, beanDefinition.getId());
            }
        }

        List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
        if (propertyValues == null || propertyValues.isEmpty()) {
            return;
        }

        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this);
        SimpleTypeConverter converter = new SimpleTypeConverter();
        try {
            for (PropertyValue propertyValue : propertyValues) {
                String propertyName = propertyValue.getName();
                Object originalValue = propertyValue.getValue();
                Object resolvedValue = valueResolver.resolveValueIfNecessary(originalValue);

                // 通过 Introspector 找到属性描述符
                BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
                for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                    if (propertyDescriptor.getName().equals(propertyName)) {
                        Object convertedValue = converter.convertIfNecessary(resolvedValue, propertyDescriptor.getPropertyType());
                        // getWriteMethod()通过反射调用 set 方法
                        propertyDescriptor.getWriteMethod().invoke(bean, convertedValue);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            throw new BeanCreationException("Failed to obtain BeanInfo for class [" + beanDefinition.getBeanClassName() + "]", e);
        }
    }

    @Override
    public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        BeanDefinition beanDefinition = this.getBeanDefinition(name);
        if (beanDefinition == null) {
            throw new NoSuchBeanDefinitionException(name);
        }
        resolveBeanClass(beanDefinition);
        return beanDefinition.getBeanClass();
    }

    @Override
    public List<Object> getBeansByType(Class<?> type) {
        List<Object> result = new ArrayList<>();
        List<String> beanIds = this.getBeanIdsByType(type);
        for (String beanId : beanIds) {
            result.add(this.getBean(beanId));
        }
        return result;
    }

    private List<String> getBeanIdsByType(Class<?> type) {
        List<String> result = new ArrayList<>();
        for (String beanName : this.beanDefinitionMap.keySet()) {
            Class<?> beanClass = null;
            try {
                beanClass = this.getType(beanName);
            } catch (Exception e) {
                logger.warn("can't load class for bean : " + beanName + ", skip it.");
                continue;
            }
            if (beanClass != null && type.isAssignableFrom(beanClass)) {
                result.add(beanName);
            }
        }
        return result;
    }

    protected Object initializeBean(BeanDefinition beanDefinition, Object bean) {
        invokeAwareMethods(bean);
        // 创建代理
        if (!beanDefinition.isSynthetic()) {
            return applyBeanPostProcessorsAfterInitialization(bean, beanDefinition.getId());
        }
        return bean;
    }

    public Object applyBeanPostProcessorsAfterInitialization(Object exisingBean, String beanName) throws BeansException {
        Object result = exisingBean;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            result = beanPostProcessor.afterInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

    private void invokeAwareMethods(final Object bean) {
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
    }

    @Override
    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    @Override
    public ClassLoader getBeanClassLoader() {
        return this.beanClassLoader != null ? this.beanClassLoader : ClassUtils.getDefaultClassLoader();
    }

    @Override
    public Object resolveDependency(DependencyDescriptor descriptor) {
        Class<?> typeToMatch = descriptor.getDependencyType();
        for (BeanDefinition beanDefinition : this.beanDefinitionMap.values()) {
            // 确保 BeanDefinition 有 Class 对象
            resolveBeanClass(beanDefinition);
            Class<?> beanClass = beanDefinition.getBeanClass();
            if (typeToMatch.isAssignableFrom(beanClass)) {
                return this.getBean(beanDefinition.getId());
            }
        }
        return null;
    }

    private void resolveBeanClass(BeanDefinition beanDefinition) {
        if (beanDefinition.hasBeanClass()) {
            return;
        } else {
            try {
                beanDefinition.resolveBeanClass(this.getBeanClassLoader());
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("can't load class: " + beanDefinition.getBeanClassName());
            }
        }
    }

}
